package edu.pku.sei.mte.serialization;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;

import edu.pku.sei.mte.constants.COMPOSITEMODE;
import edu.pku.sei.mte.mtemodel.metamodel.RTypeReference;
import edu.pku.sei.mte.mtemodel.model.MAttribute;
import edu.pku.sei.mte.mtemodel.model.MClass;
import edu.pku.sei.mte.mtemodel.model.MModelGraph;
import edu.pku.sei.mte.mtemodel.model.MReference;
import edu.pku.sei.mte.utilities.Utility;

/**
 * @author zhubin
 * 
 */
public class EcoreXMISaver {

	private EcoreLoader ecoreLoader = null;
	
	private HashMap<MClass, EObject> MClassToEObj = new HashMap<MClass, EObject>();
	
	private List<EObject> savedObject = new ArrayList<EObject>();

	public void save(MModelGraph model, String modelFileName) {
		ecoreLoader = EcoreLoader.getEcoreLoader(model.getTypeGraph());
		if(ecoreLoader == null)
			return;
		ResourceSet resourceSet = Utility.resourceSet;
		resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());
		URI fileURI = URI.createFileURI(new File(modelFileName).getAbsolutePath());
		Resource resource = resourceSet.createResource(fileURI);
		PKUMT2EMF(model, ecoreLoader, resource);
		try {
			resource.save(Collections.EMPTY_MAP);
		} catch (IOException e) {
		}
	}

	public void PKUMT2EMF(MModelGraph model, EcoreLoader ecoreLoader, Resource resource) {
		EPackage pack = ecoreLoader.getRootPackage();
		EFactory factory = pack.getEFactoryInstance();
		for(MClass mCls : model.getNodes()){
			EClass eCls = (EClass) pack.getEClassifier(mCls.getType().getName());
			EObject eObj = factory.create(eCls);
			savedObject.add(eObj);
			MClassToEObj.put(mCls, eObj);
			mapClass(mCls, eObj);
		}
		for(MReference mRef : model.getEdges()){
			mapReference(mRef);
		}
		resource.getContents().addAll(savedObject);
	}

	private void mapClass(MClass mCls, EObject eObj){
		EClass eCls = eObj.eClass();
		for(MAttribute mAttr : mCls.getAttributes()){
			eObj.eSet(eCls.getEStructuralFeature(mAttr.getName()), mAttr.getValue());
		}
	}
	
	@SuppressWarnings("unchecked")
	private void mapReference(MReference mRef){
		MClass msource = mRef.getSource();
		MClass mtarget = mRef.getTarget();
		EObject esource = MClassToEObj.get(msource);
		EObject etarget = MClassToEObj.get(mtarget);
		RTypeReference type = mRef.getType();
		EReference eref = (EReference) esource.eClass().getEStructuralFeature(type.getTargetRole());
		if(eref.getUpperBound()!=1){
			((EList<EObject>)esource.eGet(eref)).add(etarget);
		}else{
			esource.eSet(eref, etarget);
		}
		if(type.getEndComposite() == COMPOSITEMODE.SOURCE_COMPOSITE)
			savedObject.remove(etarget);
		else if(type.getEndComposite() == COMPOSITEMODE.TARGET_COMPOSITE)
			savedObject.remove(msource);
	}
}
